Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat][storage] Add Span Kind support for ES/OS #6399

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Manik2708
Copy link
Contributor

Which problem is this PR solving?

Fixes: #1923

Description of the changes

  • While querying GetOperations, operations can now be fetched with kind also. When kind kept empty, spans of all kinds are returned

How was this change tested?

  • Unit and E2E tests

Checklist

Copy link

codecov bot commented Dec 24, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.05%. Comparing base (300cc7c) to head (47598b9).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6399   +/-   ##
=======================================
  Coverage   96.04%   96.05%           
=======================================
  Files         363      363           
  Lines       20677    20719   +42     
=======================================
+ Hits        19859    19901   +42     
  Misses        624      624           
  Partials      194      194           
Flag Coverage Δ
badger_v1 9.71% <0.00%> (-0.05%) ⬇️
badger_v2 1.81% <0.00%> (-0.01%) ⬇️
cassandra-4.x-v1-manual 14.74% <0.00%> (-0.07%) ⬇️
cassandra-4.x-v2-auto 1.80% <0.00%> (-0.01%) ⬇️
cassandra-4.x-v2-manual 1.80% <0.00%> (-0.01%) ⬇️
cassandra-5.x-v1-manual 14.74% <0.00%> (-0.07%) ⬇️
cassandra-5.x-v2-auto 1.80% <0.00%> (-0.01%) ⬇️
cassandra-5.x-v2-manual 1.80% <0.00%> (-0.01%) ⬇️
elasticsearch-6.x-v1 19.03% <40.00%> (-0.13%) ⬇️
elasticsearch-7.x-v1 19.10% <40.00%> (-0.13%) ⬇️
elasticsearch-8.x-v1 19.27% <40.00%> (-0.13%) ⬇️
elasticsearch-8.x-v2 1.81% <0.00%> (-0.01%) ⬇️
grpc_v1 10.76% <0.00%> (-0.05%) ⬇️
grpc_v2 7.77% <0.00%> (-0.04%) ⬇️
kafka-3.x-v1 10.08% <0.00%> (-0.05%) ⬇️
kafka-3.x-v2 1.81% <0.00%> (-0.01%) ⬇️
memory_v2 1.81% <0.00%> (-0.01%) ⬇️
opensearch-1.x-v1 19.15% <40.00%> (-0.13%) ⬇️
opensearch-2.x-v1 19.15% <40.00%> (-0.13%) ⬇️
opensearch-2.x-v2 1.81% <0.00%> (-0.01%) ⬇️
tailsampling-processor 0.48% <0.00%> (-0.01%) ⬇️
unittests 94.93% <96.92%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@yurishkuro yurishkuro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall lgtm, but I have question on whether the whole thing could be even simpler, by treating Kind as always present (but maybe blank).

@@ -124,8 +124,9 @@ func getSpanAndServiceIndexFn(p SpanWriterParams) spanAndServiceIndexFn {
func (s *SpanWriter) WriteSpan(_ context.Context, span *model.Span) error {
spanIndexName, serviceIndexName := s.spanServiceIndex(span.StartTime)
jsonSpan := s.spanConverter.FromDomainEmbedProcess(span)
kind, _ := span.GetSpanKind()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add Kind field to the dbmodel.Span and not pass it around separately?

Copy link
Contributor Author

@Manik2708 Manik2708 Dec 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be present as a tag in json model also! I tried fetching kind from original span but failed because of the bool AllTagsAsFields. As it will already be present in the json model, should we resave it as a seperate kind also?

@Manik2708
Copy link
Contributor Author

Manik2708 commented Dec 26, 2024

overall lgtm, but I have question on whether the whole thing could be even simpler, by treating Kind as always present (but maybe blank).

Then we can't get old data! In old data Kind was not present. In the actual issue, when it was asked that What will happen to old data, it was answered that it should be accessible when kind is not present! Therefore I introduced a whole new struct with kind and saved the data without kind also.

@Manik2708 Manik2708 requested a review from yurishkuro December 26, 2024 02:05
@Manik2708
Copy link
Contributor Author

Manik2708 commented Dec 28, 2024

@yurishkuro Comitted as per your suggestions. I tried many ways but I couldn't merge those with EmptyKinds and WithoutKinds into a single aggregation. Because both bool queries are contradictory to each other and so it is behaving in a different way. The only possible way is that we have to manage them in different aggregations which will increase complexity in the Operations. So we have to decide between complexity in writing span or fetching operations.

@yurishkuro
Copy link
Member

please update the branch, I am not sure if CI is failing because of that or because of your changes. If it's the latter, how are you testing this change? Did you run e2e tests locally?

@Manik2708
Copy link
Contributor Author

Manik2708 commented Dec 29, 2024

please update the branch, I am not sure if CI is failing because of that or because of your changes. If it's the latter, how are you testing this change? Did you run e2e tests locally?

I tried approaching with empty kind but failed as query in GetOperations is becoming more complex, hence have reverted that commit.

@Manik2708
Copy link
Contributor Author

Manik2708 commented Jan 2, 2025

@yurishkuro Saving empty strings in ES is not optimal. Please see: elastic/elasticsearch#7515. I have been trying various ways of employing empty or null kinds but not getting results. The probable reason is because query is a bit complex and unconventional. I think it will be better to not to keep empty and null kinds rather handelling them seperately!

@yurishkuro
Copy link
Member

I don't have a strong opinion, but the issue you linked talks about searching for empty strings, which I don't think is the case for our scenario, we just need to write it.

@Manik2708
Copy link
Contributor Author

I don't have a strong opinion, but the issue you linked talks about searching for empty strings, which I don't think is the case for our scenario, we just need to write it.

But while reading we have to search for those operations also which have empty kinds or no kind! I am facing problems when fetching spans of all kinds, the search query behaves weirdly if I introduce filter of "". There are no problems in writing the service but in fetching them

@yurishkuro
Copy link
Member

Searching for "all kinds" to me means not specifying any filter for the "kind" field. How would it even work if you say search for kind="" when the kind is actually not empty?

@Manik2708
Copy link
Contributor Author

Manik2708 commented Jan 2, 2025

Searching for "all kinds" to me means not specifying any filter for the "kind" field. How would it even work if you say search for kind="" when the kind is actually not empty?

I think there is some sort of misunderstanding of my approach. Let me first explain problems:

  1. We want to get two fields (operatioName and kind) from our search query to ES, to achieve this, we have 3 ways:
    a) Use FetchSource(true): This will fetch all the fields which is not optimal neither is avilable in our abstraction.
    b) Use FetchSourceWithContext: Not available in our abstraction.
    c) Composite Filters: Not possible because it will be applied to a field and kind can be absent in old data.
    So I employed my approach, used the query only on service name and applied filters on kind (kinds are limited so it is possible) and used the filter name to get the kind from named buckets. But when this filter is applied to empty kind, it is not giving results. Your thoughts are absolutely correct when you said: When no kind is in query we have to just employ query on service name but we have to fetch two fields which is creating problem in just querying on service!

@yurishkuro
Copy link
Member

"Not available in our abstraction" is an odd argument since we own the abstraction and can change it at will. Re fetchsource, what is the source here - is it the whole span? Or do we write separate entries just for service/operation?

@Manik2708
Copy link
Contributor Author

Manik2708 commented Jan 2, 2025

"Not available in our abstraction" is an odd argument since we own the abstraction and can change it at will. Re fetchsource, what is the source here - is it the whole span? Or do we write separate entries just for service/operation?

Service, Operation and Kind (if present) but have to investigate the indices whether it is extracting any other information.

@Manik2708
Copy link
Contributor Author

Will try to clean this PR as soon as possible

@Manik2708
Copy link
Contributor Author

"Not available in our abstraction" is an odd argument since we own the abstraction and can change it at will. Re fetchsource, what is the source here - is it the whole span? Or do we write separate entries just for service/operation?

I have verified! It's only the service model, have comitted with FetchSourceContext

@Manik2708
Copy link
Contributor Author

@yurishkuro I have committed with FetchSourceContext, please review!

Copy link
Member

@yurishkuro yurishkuro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for delay

Query(serviceQuery).
IgnoreUnavailable(true).
Aggregation(operationsAggregation, serviceFilter)

FetchSourceContext(elastic.NewFetchSourceContext(true).Include(spanKind, operationNameField)).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this, can you please point to documentation of how this impacts the query behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#field-retrieval-methods. This part of docs suggests to use fields over source, but I used FetchSourceContext because:

  1. Weirdly olivere doesn't have Fields in their SearchService (maybe it's depreceated that's why) but rather it is in ExplainService.
  2. Technically we need the whole source as Service mapping has only three fields: Service, Kind, Operation Name.

@Manik2708 Manik2708 requested a review from yurishkuro January 21, 2025 14:33
@Manik2708 Manik2708 changed the title FEAT: Add Span Kind support for ES/OS [feat][storage] Add Span Kind support for ES/OS Jan 21, 2025
@Manik2708
Copy link
Contributor Author

@yurishkuro Please can you review this!

Signed-off-by: Manik2708 <[email protected]>
@Manik2708
Copy link
Contributor Author

Is this good to go? @yurishkuro

@mahadzaryab1 mahadzaryab1 self-requested a review February 13, 2025 14:18
@mahadzaryab1
Copy link
Collaborator

is it possible to get code coverage up?

@Manik2708
Copy link
Contributor Author

@mahadzaryab1 Have completed the code-coverage. A humble reminder to review this PR!

@yurishkuro yurishkuro requested a review from Copilot February 27, 2025 22:21
@yurishkuro
Copy link
Member

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces Span Kind support for Elasticsearch and OpenSearch, enhancing the querying capabilities of the GetOperations API. The changes include modifications to the data model, reader logic, and test cases to accommodate the new SpanKind field. Overall, the code is well-structured and addresses the issue effectively.

Summary of Findings

Merge Readiness

The code appears to be in good shape and ready for merging. The changes are well-tested and address the intended functionality. I am unable to approve the pull request, and recommend that users have others review and approve this code before merging.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Overview

This PR adds support for span kind filtering when querying operations in Elasticsearch/OpenSearch storage. Key changes include:

  • Updating service and writer methods to accept an additional span kind parameter.
  • Modifying Elasticsearch query construction in the service layer to include the span kind.
  • Updating tests and mocks as well as client and wrapper interfaces to support the new functionality.

Reviewed Changes

File Description
pkg/es/mocks/SearchService.go Added a new FetchSourceContext mock method for the updated SearchService interface.
internal/storage/v1/elasticsearch/spanstore/service_operation.go Modified Write and getOperations methods to include and handle span kind; added helper functions for converting aggregation buckets.
internal/storage/v1/elasticsearch/spanstore/reader.go Updated GetOperations to pass the span kind parameter to the storage layer.
internal/storage/v1/elasticsearch/spanstore/writer.go Updated service writer type and its invocations to forward span kind information.
pkg/es/client.go & pkg/es/wrapper/wrapper.go Extended the SearchService interface and its wrapper to include FetchSourceContext.
Integration tests (elasticsearch_test.go & opensearch_test.go) Removed the obsolete flag regarding missing span kind to validate proper behavior.

Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (4)

internal/storage/v1/elasticsearch/spanstore/service_operation.go:59

  • Update the function comment to mention the new 'kind' parameter for clarity on its usage.
func (s *ServiceOperationStorage) Write(indexName string, jsonSpan *dbmodel.Span, kind model.SpanKind) {

internal/storage/v1/elasticsearch/spanstore/reader.go:301

  • Ensure that 'query.SpanKind' is populated correctly when filtering operations by span kind to avoid unexpected results.
operations, err := s.serviceOperationStorage.getOperations(ctx, jaegerIndices, query.ServiceName, query.SpanKind, s.maxDocCount)

pkg/es/client.go:63

  • Verify that all implementations of the SearchService interface now properly handle the new FetchSourceContext method.
FetchSourceContext(fetchSourceContext *elastic.FetchSourceContext) SearchService

pkg/es/wrapper/wrapper.go:268

  • Consider adding unit tests for the new FetchSourceContext wrapper method to ensure it correctly chains and returns the expected SearchService.
func (s SearchServiceWrapper) FetchSourceContext(fetchSourceContext *elastic.FetchSourceContext) es.SearchService {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ES storage plugin: query service to support spanKind when retrieve operations for a given service
3 participants